package cn.iocoder.yudao.module.security.service.whitelistevent;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.security.controller.admin.whitelistevent.vo.WhitelistEventPageReqVO;
import cn.iocoder.yudao.module.security.controller.admin.whitelistevent.vo.WhitelistEventSaveReqVO;
import cn.iocoder.yudao.module.security.dal.dataobject.whitelistevent.WhitelistEventDO;
import cn.iocoder.yudao.module.security.dal.dataobject.whitelistevent.WhitelistEventEsDO;
import cn.iocoder.yudao.module.security.dal.dataobject.whitelistoperate.WhitelistOperateDO;
import cn.iocoder.yudao.module.security.dal.mysql.whitelistevent.WhitelistEventMapper;
import cn.iocoder.yudao.module.security.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.security.service.dashboard.SecurityCommonService;
import cn.iocoder.yudao.module.security.service.whitelistoperate.WhitelistOperateService;
import cn.iocoder.yudao.module.security.util.IpUtils;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.AggregationBuilders;
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsBucket;
import co.elastic.clients.elasticsearch._types.aggregations.TermsAggregation;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.util.NamedValue;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.security.enums.ErrorCodeConstants.WHITELIST_EVENT_NOT_EXISTS;

/**
 * 白名单事件 Service 实现类
 *
 * @author cai
 */
@Service
@Validated
public class WhitelistEventServiceImpl implements WhitelistEventService {

    private static final Logger log = LoggerFactory.getLogger(WhitelistEventServiceImpl.class);
    @Resource
    private WhitelistEventMapper whitelistEventMapper;

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private WhitelistOperateService whitelistOperateService;

    @Resource
    private SecurityCommonService securityCommonService;

    @Override
    public Long createWhitelistEvent(WhitelistEventSaveReqVO createReqVO) {
        // 插入
        WhitelistEventDO whitelistEvent = BeanUtils.toBean(createReqVO, WhitelistEventDO.class);
        whitelistEventMapper.insert(whitelistEvent);
        // 返回
        return whitelistEvent.getId();
    }

    @Override
    public void updateWhitelistEvent(WhitelistEventSaveReqVO updateReqVO) {
        // 校验存在
        validateWhitelistEventExists(updateReqVO.getId());
        // 更新
        WhitelistEventDO updateObj = BeanUtils.toBean(updateReqVO, WhitelistEventDO.class);
        whitelistEventMapper.updateById(updateObj);
    }

    @Override
    public void deleteWhitelistEvent(Long id) {
        // 校验存在
        validateWhitelistEventExists(id);
        // 删除
        whitelistEventMapper.deleteById(id);
    }

    private void validateWhitelistEventExists(Long id) {
        if (whitelistEventMapper.selectById(id) == null) {
            throw exception(WHITELIST_EVENT_NOT_EXISTS);
        }
    }

    @Override
    public WhitelistEventDO getWhitelistEvent(Long id) {
        return whitelistEventMapper.selectById(id);
    }

    @Override
    public PageResult<WhitelistEventDO> getWhitelistEventPage(WhitelistEventPageReqVO pageReqVO) {

        if (StringUtils.isNotBlank(pageReqVO.getSourceIp())) {
            if (!IpUtils.isIpv4Address(pageReqVO.getSourceIp())) {
                throw exception(ErrorCodeConstants.SECURITY_DEFAULT_ERROR, "源ip地址格式不正确");
            }
        }
        if (StringUtils.isNotBlank(pageReqVO.getDestinationIp())) {
            if (!IpUtils.isIpv4Address(pageReqVO.getDestinationIp())) {
                throw exception(ErrorCodeConstants.SECURITY_DEFAULT_ERROR, "目的ip地址格式不正确");
            }
        }

        getWhitelistEventAgg();
        WhitelistOperateDO latestWhitelistOperate = whitelistOperateService.getLatestWhitelistOperate();
        if (Objects.isNull(latestWhitelistOperate)) {
            return PageResult.empty();
        }

        String startTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getStartTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";
        String endTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getEndTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";

        Integer from = (pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize();
        Integer size = pageReqVO.getPageSize();
        // 构建复合查询
        FieldValue s7COMM = FieldValue.of("S7COMM");
        FieldValue dnp3 = FieldValue.of("DNP3");
        FieldValue modbus = FieldValue.of("MODBUS");
        List<FieldValue> list = new ArrayList<>();
        list.add(s7COMM);
        list.add(dnp3);
        list.add(modbus);
        SearchRequest request = SearchRequest.of(s -> s
                        .index(".ds-logs-pfelk-snort-*")
                        .query(q -> q
                                .bool(b -> {
                                    b.filter(f -> f.terms(t -> t.field("network.app")
                                                    .terms(v -> v.value(list))))
                                            .filter(f -> f.range(r -> r.date(a -> a.field("@timestamp")
                                                    .gte(startTimeStr)
                                                    .lte(endTimeStr))));
                                    //.filter(f -> f.term(t -> t.field("elastalert.alert").value(0)))


                                    // 如果 ip 不为空，添加 ip 过滤条件
                                    if (StringUtils.isNotBlank(pageReqVO.getSourceIp())) {
                                        b.filter(f -> f.term(t -> t.field("source.ip").value(pageReqVO.getSourceIp())));
                                    }
                                    if (StringUtils.isNotBlank(pageReqVO.getDestinationIp())) {
                                        b.filter(f -> f.term(t -> t.field("destination.ip").value(pageReqVO.getDestinationIp())));
                                    }
                                    if (Objects.nonNull(pageReqVO.getSourcePort())) {
                                        b.filter(f -> f.term(t -> t.field("source.port").value(pageReqVO.getSourcePort())));
                                    }
                                    if (Objects.nonNull(pageReqVO.getDestinationPort())) {
                                        b.filter(f -> f.term(t -> t.field("destination.port").value(pageReqVO.getDestinationPort())));
                                    }
                                    if (StringUtils.isNotBlank(pageReqVO.getProtocol())) {
                                        b.filter(f -> f.term(t -> t.field("network.app").value(pageReqVO.getProtocol())));
                                    }
                                    if (Objects.nonNull(pageReqVO.getFunctionCode())) {
                                        b.filter(f -> f.term(t -> t.field("network.func").value(pageReqVO.getFunctionCode())));
                                    }
                                    return b;
                                })
                        )
                        .from(from)
                        .size(size)
                        .sort(so -> so.field(f -> f.field("@timestamp").order(SortOrder.Desc)))
                //.sort(so -> so.field(f -> f.field("event.sequence").order(SortOrder.Asc)))
        );

        // 执行并处理结果
        try {
            SearchResponse<Map> response = elasticsearchClient.search(request, Map.class);
            HitsMetadata<Map> hits = response.hits();
            long total = hits.total().value();
            List<WhitelistEventDO> list1 = new ArrayList<>();
            for (Hit<Map> hit : hits.hits()) {
                String sourceStr = JSON.toJSONString(hit.source());
                //WhitelistEventEsDO source = JSON.parseObject(sourceStr, WhitelistEventEsDO.class);
                WhitelistEventDO.WhitelistEventDOBuilder builder = WhitelistEventDO.builder();
                WhitelistEventEsDO.SourceData sourceData = JSON.parseObject(sourceStr, WhitelistEventEsDO.SourceData.class);
                if (Objects.nonNull(sourceData)) {
                    if (Objects.nonNull(sourceData.getSource())) {
                        builder.sourcePort(Integer.valueOf(sourceData.getSource().getPort()));
                        builder.sourceIp(sourceData.getSource().getIp());
                    }
                    if (Objects.nonNull(sourceData.getDestination())) {
                        builder.destinationPort(Integer.valueOf(sourceData.getDestination().getPort()));
                        builder.destinationIp(sourceData.getDestination().getIp());
                    }
                    if (Objects.nonNull(sourceData.getNetwork())) {
                        builder.protocol(sourceData.getNetwork().getApp())
                                .functionCode(Integer.valueOf(sourceData.getNetwork().getFunc()))
                                .functionCodeCn(sourceData.getNetwork().getFuncCn());
                        if (Objects.nonNull(sourceData.getNetwork().getMsgType()) && NumberUtils.isCreatable(sourceData.getNetwork().getMsgType())) {
                            builder.msgType(Integer.valueOf(sourceData.getNetwork().getMsgType()));
                        }
                        if (Objects.nonNull(sourceData.getNetwork().getRegisterArea()) && NumberUtils.isCreatable(sourceData.getNetwork().getRegisterArea())) {
                            builder.registerArea(Integer.valueOf(sourceData.getNetwork().getRegisterArea()));
                        }
                        if (Objects.nonNull(sourceData.getNetwork().getDbArea()) && NumberUtils.isCreatable(sourceData.getNetwork().getDbArea())) {
                            builder.dbArea(Integer.valueOf(sourceData.getNetwork().getDbArea()));
                        }
                        if (Objects.nonNull(sourceData.getNetwork().getPointType()) && NumberUtils.isCreatable(sourceData.getNetwork().getPointType())) {
                            builder.pointType(Integer.valueOf(sourceData.getNetwork().getPointType()));
                        }
                        if (Objects.nonNull(sourceData.getNetwork().getObjectGroupNum()) && NumberUtils.isCreatable(sourceData.getNetwork().getObjectGroupNum())) {
                            builder.objectGroupNum(Integer.valueOf(sourceData.getNetwork().getObjectGroupNum()));
                        }
                        if (Objects.nonNull(sourceData.getNetwork().getVariantNum()) && NumberUtils.isCreatable(sourceData.getNetwork().getVariantNum())) {
                            builder.variantNum(Integer.valueOf(sourceData.getNetwork().getVariantNum()));
                        }
                    }
                    String origin = sourceData.getTimestamp();
                    int dotIndex = origin.indexOf('.');
                    String result = origin.substring(0, dotIndex);
                    LocalDateTime timestamp = LocalDateTime.parse(result, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
                    builder.eventTime(timestamp);
                }

                WhitelistEventDO build = builder.build();
                list1.add(build);
            }
            return new PageResult<>(list1, total);
        } catch (IOException e) {
            log.error("elasticsearch request err,request: {}", request, e);
        }

        return PageResult.empty();
    }

    public List<String> getWhitelistEventAgg() {
        WhitelistOperateDO latestWhitelistOperate = whitelistOperateService.getLatestWhitelistOperate();
        return getWhitelistEventAgg(latestWhitelistOperate);
    }

    public List<String> getWhitelistEventAgg(WhitelistOperateDO latestWhitelistOperate) {
        if (Objects.isNull(latestWhitelistOperate)) {
            return Collections.emptyList();
        }

        String startTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getStartTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";
        String endTimeStr = LocalDateTimeUtil.format(latestWhitelistOperate.getEndTime(), DatePattern.UTC_SIMPLE_PATTERN) + "+08:00";

        // 构建复合查询
        FieldValue s7COMM = FieldValue.of("S7COMM");
        FieldValue dnp3 = FieldValue.of("DNP3");
        List<FieldValue> list = new ArrayList<>();
        list.add(s7COMM);
        list.add(dnp3);
        // 4. 构建聚合分析
        TermsAggregation termsAggregation = AggregationBuilders.terms()
                .field("network.main_info.keyword")
                .size(90000)
                .order(new NamedValue<SortOrder>("_count", SortOrder.Desc))
                .build();
        SearchRequest request = SearchRequest.of(s -> s
                .index(".ds-logs-pfelk-snort-*")
                //.index("elastalert_log")
                .query(q -> q
                        .bool(b -> b
                                        .filter(f -> f.terms(t -> t.field("network.app.keyword")
                                                .terms(v -> v.value(list))))
                                        .filter(f -> f.range(r -> r.date(a -> a.field("@timestamp")
                                                .gte(startTimeStr)
                                                .lte(endTimeStr))))
                                //.filter(f -> f.term(t -> t.field("elastalert.alert").value(0)))
                        )
                )
                .aggregations("main_info_analysis", termsAggregation._toAggregation())
                .size(0)
        );
        List<String> list2 = new ArrayList<>();
        // 执行并处理结果
        try {
            SearchResponse<Object> response = elasticsearchClient.search(request, Object.class);

            // 7. 解析聚合结果
            List<StringTermsBucket> buckets = response.aggregations()
                    .get("main_info_analysis")
                    .sterms()
                    .buckets()
                    .array();
            for (StringTermsBucket bucket : buckets) {
                String key = bucket.key().stringValue();
                list2.add(key);
            }
        } catch (IOException e) {
            log.error("elasticsearch request err,request: {}", request, e);
        }

        return list2;
    }

}